import { clone, deepMerge, isFunction, isString, setObjToUrlParams } from '@vc/utils';
import type { AxiosInstance, AxiosResponse } from 'axios';
import axios from 'axios';
import { context } from '../bridge';
import { VAxios } from './Axios';
import { AxiosRetry } from './axiosRetry';
import type { AxiosTransform, CreateAxiosOptions } from './axiosTransform';
import { checkStatus } from './checkStatus';
import sys, { ContentTypeEnum, RequestEnum, ResultEnum } from './constants';
import { formatRequestDate, joinTimestamp } from './helper';
import type { Recordable, RequestOptions } from './types';

const transform: AxiosTransform = {
  transformResponseHook: (res: AxiosResponse, options: RequestOptions) => {
    const { isTransformResponse, isReturnNativeResponse } = options;

    if (isReturnNativeResponse) {
      return res;
    }

    if (!isTransformResponse) {
      return res.data;
    }

    if (!res.data) {
      throw new Error(sys.apiRequestFailed);
    }
  
    // const { code, data, msg } = res.data;
    const { code, data, message } = context.responseParams!;
  
    // 这里逻辑可以根据项目进行修改 判断请求数据是否成功
    const isSuccess = res.data[code] === ResultEnum.SUCCESS;
  
    if (isSuccess) {
      return res.data[data];
    }
  
    throw new Error(res.data[message] || sys.apiRequestFailed);
  },

  beforeRequestHook: (config, options) => {
    const { apiUrl, joinParamsToUrl, formatDate, joinTime = true, urlPrefix } = options;
  
    if (urlPrefix) {
      config.url = `${urlPrefix}${config.url}`;
    }
    if (apiUrl) {
      const _apiUrl = isString(apiUrl) ? apiUrl : isFunction(apiUrl) ? apiUrl?.() : '';

      config.url = `${_apiUrl}${config.url}`;
    }

    const params = config.params || {};
    const data = config.data || false;

    formatDate && data && !isString(data) && formatRequestDate(data);

    if (config.method?.toUpperCase() === RequestEnum.GET) {
      if (!isString(params)) {
        config.params = Object.assign(params || {}, joinTimestamp(joinTime, false));
      } else {
        config.url = config.url + params + `${joinTimestamp(joinTime, true)}`;
        config.params = undefined;
      }
    } else {
      if (!isString(params)) {
        formatDate && formatRequestDate(params);
        if (
          Reflect.has(config, 'data') &&
          config.data &&
          (Object.keys(config.data).length > 0 || config.data instanceof FormData)
        ) {
          config.data = data;
          config.params = params;
        } else {
          config.data = params;
          config.params = undefined;
        }
        if (joinParamsToUrl) {
          config.url = setObjToUrlParams(config.url as string, Object.assign({}, config.params, config.data));
        }
      } else {
        config.url = config.url + params;
        config.params = undefined;
      }
    }
    return config;
  },

  requestInterceptors: (config: CreateAxiosOptions, options) => {
    const token = context.getTokenFunction?.();

    if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
      const _authenticationScheme = config?.authenticationScheme ?? options?.authenticationScheme;

      (config as Recordable).headers.Authorization = _authenticationScheme
        ? `${_authenticationScheme} ${token}`
        : token;
    }
    return config;
  },

  responseInterceptors: (res: AxiosResponse<any>) => {
    return res;
  },

  responseInterceptorsCatch: (axiosInstance: AxiosResponse, error: any) => {
    const { message: responseMsg } = context.responseParams!;
    // 调用全局的响应错误拦截器
    const { response, code, message, config } = error || {};
  
    const msg: string = response?.data?.[responseMsg] ?? '';
    const err: string = error?.toString?.() ?? '';
    let errMessage = '';

    if (axios.isCancel(error)) {
      return Promise.reject(error);
    }

    if (!config.requestOptions.errorMessageCustom) {
      if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) {
        errMessage = sys.apiTimeoutMessage;
      }

      if (err?.includes('Network Error')) {
        errMessage = sys.networkExceptionMsg;
      }

      if (errMessage) {
        throw new Error(errMessage);
      }

      checkStatus(error?.response?.status, msg);
    }

    const retryRequest = new AxiosRetry();

    const { isOpenRetry } = config.requestOptions.retryRequest;
    config.method?.toUpperCase() === RequestEnum.GET &&
      isOpenRetry &&
      retryRequest.retry(axiosInstance as unknown as AxiosInstance, error);

    return Promise.reject(error);
  }
};

export const createAxios = (opt?: Partial<CreateAxiosOptions>, callback?: Function): VAxios => {
  const requestConfig = deepMerge(
    {
      timeout: 30 * 1000,
      headers: { 'Content-Type': ContentTypeEnum.JSON },
      // 如果是form-data格式
      // headers: { 'Content-Type': ContentTypeEnum.FORM_URLENCODED },
      // 数据处理方式
      transform: clone(transform),
      // token 请求头
      authenticationScheme: 'Bearer',
      // 配置项，下面的选项都可以在独立的接口请求中覆盖
      requestOptions: {
        // 是否返回原生响应头 比如：需要获取响应头时使用该属性
        isReturnNativeResponse: false,
        // 需要对返回数据进行处理
        isTransformResponse: true,
        // post请求的时候添加参数到url
        joinParamsToUrl: false,
        // 格式化提交参数时间
        formatDate: false,
        // 接口地址
        apiUrl: () => context.apiUrl,
        // 是否加入时间戳 get 请求使用
        joinTime: false,
        urlPrefix: '',
        // 忽略重复请求
        ignoreCancelToken: true,
        // 是否携带token
        withToken: true,
        retryRequest: {
          isOpenRetry: false,
          count: 5,
          waitTime: 100
        },
        errorMessageCustom: false
      }
    },
    opt || {}
  );

  if (callback) {
    return callback(requestConfig);
  } else {
    return new VAxios(requestConfig);
  }
};

export const request = createAxios();
